package jee.boot.module.act.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import jee.boot.common.basic.CrudService;
import jee.boot.common.basic.Page;
import jee.boot.common.basic.ServiceException;
import jee.boot.common.basic.result.MapData;
import jee.boot.common.utils.Encodes;
import jee.boot.entity.activiti.ActProcDef;
import jee.boot.entity.system.Filter;
import jee.boot.module.act.dao.ActProcDefDao;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class ActProcDefService extends CrudService<ActProcDefDao, ActProcDef> {
    @Autowired
    private RepositoryService repositoryService;

    @Override
    public List<ActProcDef> findList(ActProcDef entity) {
        // 增加默认过滤条件,查询时候只查询版本最新的记录
        entity.addFilter(new Filter("NOT EXISTS ( SELECT 1 FROM act_re_procdef WHERE KEY_ = a.KEY_ AND VERSION_ > a.VERSION_ )","built-in",null));
        return super.findList(entity);
    }

    @Override
    public Page<ActProcDef> findPage(ActProcDef entity) {
        // 增加默认过滤条件,查询时候只查询版本最新的记录
        entity.addFilter(new Filter("NOT EXISTS ( SELECT 1 FROM act_re_procdef WHERE KEY_ = a.KEY_ AND VERSION_ > a.VERSION_ )","built-in",null));
        return super.findPage(entity);
    }

    /**
     * 部署
     *
     * @param file
     */
    public void deploy(String category, MultipartFile file) {
        String fileName = file.getOriginalFilename();
        try {
            InputStream fileInputStream = file.getInputStream();
            Deployment deployment = null;
            if (fileName.indexOf("bpmn20.xml") != -1) {
                deployment = repositoryService.createDeployment().category(category).addInputStream(fileName, fileInputStream).deploy();
            } else {
                throw new ServiceException(1443);
            }
            List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
            if (list.size() == 0) {
                throw new ServiceException(1444);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new ServiceException(1444, e);
        }
    }

    /**
     * 挂起、激活流程实例
     */
    public void updateStatus(ActProcDef entity) {
        switch (entity.getSuspensionState()) {
            case 1://激活
                repositoryService.activateProcessDefinitionById(entity.getId(), true, new Date());
                break;
            case 2://挂起
                repositoryService.suspendProcessDefinitionById(entity.getId(), true, new Date());
                break;
            default:
                break;
        }

    }

    /**
     * 删除部署的流程
     */
    public void deleteDeployment(ActProcDef entity) {
        repositoryService.deleteDeployment(entity.getDeployment().getId());
    }


    /**
     * 导出xml文件
     *
     * @param entity
     * @param response
     */
    public void exportXml(ActProcDef entity, HttpServletResponse response) {
        try {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(entity.getId()).singleResult();
            // 查询资源文件
            InputStream resourceAsStream = repositoryService.getProcessModel(processDefinition.getId());
            //读取输入流
            int count = resourceAsStream.available();
            byte[] bc = new byte[count];
            resourceAsStream.read(bc);
            ByteArrayInputStream in = new ByteArrayInputStream(bc);
            IOUtils.copy(in, response.getOutputStream());
            response.setContentType("application/octet-stream; charset=utf-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + Encodes.urlEncode(processDefinition.getResourceName()));
            response.flushBuffer();
        } catch (Exception e) {
            logger.error(e.getMessage(),e);
            throw new ServiceException(1441, MapData.instance().put("modelId",entity.getId()).data());
        }
    }

    /**
     * 导出图片
     *
     * @param entity
     * @param response
     */
    public void exportPng(ActProcDef entity, HttpServletResponse response) {
        try {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(entity.getId()).singleResult();
            BpmnModel bpmnModel = repositoryService.getBpmnModel(entity.getId());
            ProcessDiagramGenerator p = new DefaultProcessDiagramGenerator();
            InputStream resourceAsStream = p.generateDiagram(bpmnModel, "png", new ArrayList<String>(), new ArrayList<String>(), "宋体", "宋体", "宋体", null, 1.0);
            //读取输入流
            int count = resourceAsStream.available();
            byte[] bc = new byte[count];
            resourceAsStream.read(bc);
            ByteArrayInputStream in = new ByteArrayInputStream(bc);
            IOUtils.copy(in, response.getOutputStream());
            response.setContentType("image/png");
            response.flushBuffer();
        } catch (Exception e) {
            logger.error(e.getMessage(),e);
            throw new ServiceException(1442, MapData.instance().put("modelId",entity.getId()).data());
        }
    }

    /**
     * 转换成模型
     *
     * @param entity
     */
    public void convertToModel(ActProcDef entity) {
        try {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(entity.getId()).singleResult();
            InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                    processDefinition.getResourceName());
            XMLInputFactory xif = XMLInputFactory.newInstance();
            InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
            XMLStreamReader xtr = xif.createXMLStreamReader(in);
            BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

            BpmnJsonConverter converter = new BpmnJsonConverter();
            ObjectNode modelNode = converter.convertToJson(bpmnModel);
            org.activiti.engine.repository.Model modelData = repositoryService.newModel();
            modelData.setKey(processDefinition.getKey());
            modelData.setName(processDefinition.getResourceName());
            modelData.setCategory(processDefinition.getCategory());//.getDeploymentId());
            modelData.setDeploymentId(processDefinition.getDeploymentId());
            modelData.setVersion(Integer.parseInt(String.valueOf(repositoryService.createModelQuery().modelKey(modelData.getKey()).count() + 1)));

            ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
            modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
            modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, modelData.getVersion());
            modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
            modelData.setMetaInfo(modelObjectNode.toString());

            repositoryService.saveModel(modelData);

            repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new ServiceException(1445);
        }
    }
}
